package fr.vvins.core.search.tools;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import fr.vvins.core.search.SearchEngine;
import fr.vvins.core.search.SearchEngineException;
import fr.vvins.core.search.query.SearchFacetResult;
import fr.vvins.core.search.query.SearchQuery;
import fr.vvins.core.search.query.SearchResult;
import fr.vvins.core.search.query.SuggestQuery;

/**
 * Search tools implementation.
 * 
 * @author fguay
 */
public class SearchToolsImpl implements SearchTools {

    private static final String TOKEN_ALL = "*";
    
    private static final String FIELD_ENTITY = "entity:";
    
    private static final String FIELD_TEXT = "text:";
   
    private boolean startWithSearch;

    public SearchEngine searchManager;

    private final static Logger log = LoggerFactory.getLogger(SearchToolsImpl.class);

    
    /**
     * @see fr.SearchTools.search.tools.ISearchTools#searchAll(fr.canal.base.user.Profile,
     *      java.lang.String, int)
     */
    @Override
	public SearchResult searchAll(String field, int pStart, int pMax) throws SearchEngineException {
        SearchQuery query = new SearchQuery();
        query.setMax(pMax);
        query.setStart(pStart);
        query.setQuery(prepareQuery(FIELD_TEXT, field));
        return searchManager.executeQuery(query);
    }
    
    
    @Override
 	public SearchResult search(String query) throws SearchEngineException {
         return searchManager.executeQuery(query);
     }
   
    @Override
	public SearchFacetResult searchAllByFacet(String pField, List<String> pFacets, Map<String, String> pFacetLimit, Map<String,Map<String,Long>> pFacetsRange, int pStart, int pMax) throws SearchEngineException {
    	SearchQuery query = new SearchQuery();
    	query.setMax(pMax);
    	query.setFacet(true);
    	query.setFacetFields(pFacets);
    	query.setFilterQuery(buildFacetFilter(pFacetLimit));
    	if(pField != null){
    		query.setQuery(prepareQuery(FIELD_TEXT, pField));
    	}
    	if(pFacetsRange != null){
    		query.setRangeLimit(pFacetsRange);
    	}
    	return searchManager.executeFacetQuery(query);
    }

    
    /**
     * @see fr.SearchTools.search.tools.ISearchTools#searchAll(fr.canal.base.user.Profile,
     *      java.lang.String, int)
     */
    @Override
	public SearchResult searchEntity(String pEntity, String field, int pStart, int pMax) throws SearchEngineException {
        SearchQuery query = new SearchQuery();
        query.setMax(pMax);
        query.setStart(pStart);
        query.setQuery(prepareQuery(FIELD_TEXT, field));
        query.addFilterQuery(buildEntityFilter(pEntity));
        return searchManager.executeQuery(query);
    }

   
    @Override
	public SearchFacetResult searchEntityByFacet(String pEntity, String pField, List<String> pFacets,  Map<String, String> pFacetLimit, Map<String,Map<String,Long>> pFacetsRange, int pStart, int pMax) throws SearchEngineException {
    	SearchQuery query = new SearchQuery();
    	query.setMax(pMax);
    	query.setFacet(true);
    	query.setFacetFields(pFacets);
    	query.setFilterQuery(buildFacetFilter(pFacetLimit));
    	query.addFilterQuery(buildEntityFilter(pEntity));
    	if(pField != null){
    		query.setQuery(prepareQuery(FIELD_TEXT, pField));
    	}
    	if(pFacetsRange != null){
    		query.setRangeLimit(pFacetsRange);
    	}
    	return searchManager.executeFacetQuery(query);
    }

    
    /**
     * @see fr.SearchTools.search.tools.ISearchTools#suggest(java.lang.String)
     */
    @Override
	public List<String> suggest(String field, int max) throws SearchEngineException {
        SuggestQuery query = new SuggestQuery();
        query.setTerm(field.toLowerCase());
        query.setLimit(max);
        return searchManager.suggest(query);
    }
    
    /**
     * @see fr.SearchTools.search.tools.ISearchTools#suggest(java.lang.String)
     */
    @Override
	public List<String> suggestEntity(String pEntity, String pField, int pMax) throws SearchEngineException {
        SuggestQuery query = new SuggestQuery();
        query.setTerm(pField.toLowerCase());
        query.setLimit(pMax);
        return searchManager.suggest(query);
    }

    
    private List<String> buildFacetFilter(Map<String,String> pFacetLimit){
    	if(pFacetLimit == null || pFacetLimit.isEmpty()){
    		return null;
    	} 
    	List<String> facetLimit = new ArrayList<String>();
    	for(String facet : pFacetLimit.keySet()){
    		facetLimit.add(buildFacetFilter(facet, pFacetLimit.get(facet)));
    	}
    	return facetLimit;
    }
    
    
    
    protected String buildFacetFilter(String field, String value){
    	StringBuffer buffer = new StringBuffer();
    	if(field != null && value != null){
	    	if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)){
	    		List<String> values = new ArrayList<String>();
	    		if(value.contains(",")){
	    			values = Arrays.asList(value.split(","));
	    		} else {
	    			values.add(value);
	    		}
	    		
	    		for(Iterator<String> itValues = values.iterator(); itValues.hasNext();){
	    			String fieldValue = itValues.next();
	    			buffer.append("(").append(field).append(":").append(fieldValue).append(")");
	    			if(itValues.hasNext()){
	    				buffer.append(" OR ");
	    			}
	    		}
	    		//buffer.append("{!raw f=").append(field).append("}").append(value);
	    		//buffer.append(field).append(":").append(value);
	    	}
    	}
    	return buffer.toString();
    }
    
    protected String buildEntityFilter(String entity){
    	StringBuffer buffer = new StringBuffer();
    	entity = StringUtils.capitalize(entity);
    	buffer.append("(").append(FIELD_ENTITY).append(entity).append(")");
        return buffer.toString();
    }
    

    /*private void printResult(SearchResult searchs) {
        log.debug("Size : " + searchs.getSize());
        for (Entity item : searchs.getResults()) {
            log.debug(item.getId() + " : " + item.getEntityName());
        }
    }*/
    

    private String prepareQuery(String field, String valueField) {
        StringBuffer buffer = new StringBuffer();
        if (valueField == null) {
            valueField = "";
        }
        if (valueField.length() == 0) {
            buffer.append(field).append(TOKEN_ALL);
        } else {
            valueField = valueField.trim().toLowerCase().replaceAll("'", " ").replaceAll(" +", " ");
            buffer.append("((").append(field).append(valueField).append(") OR ");
            buffer.append("(").append(field).append(valueField).append(TOKEN_ALL).append("))");
        }
        return buffer.toString();
    }



    public SearchEngine getSearchManager() {
        return searchManager;
    }

    public void setSearchManager(SearchEngine pSearchManager) {
        searchManager = pSearchManager;
    }


    public boolean isStartWithSearch() {
        return startWithSearch;
    }

    public void setStartWithSearch(boolean pStartWithSearch) {
    	startWithSearch = pStartWithSearch;
    }

}
